Exploración de datos

Una de las buenas prácticas que se recomienda implementar a la hora de analizar datos, es iniciar haciendo una mirada a estos de manera que desde el inicio, se pueda tener un visión general de la materia prima con la que se pretende trabajar.

¿Qué interesa conocer de los datos?

Cada analista podría definir sus propias prioridades, pero posiblemente se pueda tener un consenso en lo siguiente: tipos de datos, las variables, datos atípicos, celdas vacías, tamaño de la base de datos, estructura general de la base de datos, entre otras.

En R podemos echar mano de varias herramientas exploratorias de consulta o de tipo gráficas, que rápidamente permiten tener ese panorama general.

Una mirada a los datos

Primeramente creamos un primer chunk para cargar los paquetes o librerías de R requeridos para el trabajo con los datos:

library(visdat) # Paquete con varias herramientas para visualizar datos
library(DataExplorer) #Para usar la función plot_missing
library(mosaicData) # Paquete que contiene bases de datos 
library(ggplot2) # paquete graficador de alto desempeño
library(colorspace) # Paleta de colores
library(dplyr) #para usar funciones de ordenamiento, selección, agrupación
library(GGally)
library(tidyr) # para usar la función drop_na
library(PerformanceAnalytics)
library(psych)
library(ggraph)
library(plotly) #para crear gráficos interactivos
library(datos) # paquete que contienes diferentes bases de datos

R posee algunas bases de datos de uso libre, de las cuáles en esta oportunidad se cargará la base de datos airquality la cuál contiene datos ambientales.

En RStudio podemos consultar la ayuda si se desea saber información sobre algún paquete, una función o alguna base de datos. La siguiente instrucción permite acceder a la ayuda para conocer cómo está cosntituida la base de datos airquality:

?airquality
## starting httpd help server ... done

Se puede ver un resumen sobre como está constituida dicha base de datos con las siguientes indicaciones:

head(airquality)
summary(airquality)
##      Ozone           Solar.R           Wind             Temp      
##  Min.   :  1.00   Min.   :  7.0   Min.   : 1.700   Min.   :56.00  
##  1st Qu.: 18.00   1st Qu.:115.8   1st Qu.: 7.400   1st Qu.:72.00  
##  Median : 31.50   Median :205.0   Median : 9.700   Median :79.00  
##  Mean   : 42.13   Mean   :185.9   Mean   : 9.958   Mean   :77.88  
##  3rd Qu.: 63.25   3rd Qu.:258.8   3rd Qu.:11.500   3rd Qu.:85.00  
##  Max.   :168.00   Max.   :334.0   Max.   :20.700   Max.   :97.00  
##  NA's   :37       NA's   :7                                       
##      Month            Day      
##  Min.   :5.000   Min.   : 1.0  
##  1st Qu.:6.000   1st Qu.: 8.0  
##  Median :7.000   Median :16.0  
##  Mean   :6.993   Mean   :15.8  
##  3rd Qu.:8.000   3rd Qu.:23.0  
##  Max.   :9.000   Max.   :31.0  
## 
str(airquality)
## 'data.frame':    153 obs. of  6 variables:
##  $ Ozone  : int  41 36 12 18 NA 28 23 19 8 NA ...
##  $ Solar.R: int  190 118 149 313 NA NA 299 99 19 194 ...
##  $ Wind   : num  7.4 8 12.6 11.5 14.3 14.9 8.6 13.8 20.1 8.6 ...
##  $ Temp   : int  67 72 74 62 56 66 65 59 61 69 ...
##  $ Month  : int  5 5 5 5 5 5 5 5 5 5 ...
##  $ Day    : int  1 2 3 4 5 6 7 8 9 10 ...

Usando el paquete visdat, hacemos una exploración gráfica de la base de datos:

vis_dat(airquality)
## Warning: `gather_()` was deprecated in tidyr 1.2.0.
## Please use `gather()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

vis_guess(airquality)

vis_miss(airquality)

plot_missing(airquality) # Me indica si tenemos datos en blanco y me da una recomendación, que en este caso dice que es "Good y Ok", lo cual no es mucho problema

# Use ?plot_missing() si desea conocer más sobre esta función

Si se desea, se pueden omitir los datos NA, pero al implicar este procedimiento una modificación de la base de datos original, se recomienda crear una nueva variable de almacenamiento, como se muestra en el siguiente chunk:

aire <- na.omit(airquality)
vis_dat(aire)

Atipicidades

Para el estudio de las tipicidades, se echará mano de dos tipos de gráficos del paquete ggplot2: Diagramas de caja (boxplot) y gráficos de violín (Violinplot).

El paquete ggplot2 es uno de los paquetes más completos que tiene R para generar gráficos diversos y de alta complejidad y calidad, el cuál trabaja bajo una filosofía de Grammar of Graphs (Gramática de gráficos) que permite contruir los gráficos a partir de capas.

Primeramente se crea el marco de trabajo base (Primera capa), el cuál al contener solo la base de datos cargada, no mostrará ningún elemento visual:

ggplot(aire, aes(Month)) 

Ahora, se agrega una segunda capa de tipo geom_boxplot

ggplot(aire, aes(Month,Ozone )) +
  geom_boxplot(alpha = 0.4, color = "blue", aes(group = cut_width(Month, 1)))

En el gráfico anterior se puede notar que en el horizontal aparecen los meses dados por número de mes, pero podría ser interesante verlo con nombre del mes. El siguiente código crea una nueva columna en la base de datos con el nombre del mes

aire$Mes <- ifelse(aire$Month == 5, "Mayo", 
                   ifelse (aire$Month == 6, "Junio", 
                           ifelse(aire$Month == 7,"Julio", 
                                  ifelse(aire$Month == 8, "Agosto","Septiembre")
                                  )
                           )
                   )

aire$Mes <- factor(aire$Mes, levels = c("Mayo","Junio","Julio","Agosto","Septiembre"))

Seguidamente crearemos el mismo diagrama de caja, pero usando la nueva columnna

ggplot(aire, aes(Mes, Ozone)) + 
  geom_boxplot(alpha = 0.4, color = "dodgerblue4", outlier.shape = 19) 

Los datos atípicos se muestran como puntos fuera de la caja, pero esa forma podría cambiar a gusto del usuario. En el siguiente enlace puede ver un compendio de variantes para la figura que podría usarse para representar los puntos en R

Figuras para puntos ggplot2

La siguiente variante del diagrama de caja puede ser más interesante:

ggplot(aire, aes(Mes, Ozone)) + 
  geom_boxplot(alpha = 0.4, color = "dodgerblue4", outlier.shape = 19, outlier.color = "red", aes(fill = Month)) +
  geom_point(alpha = 0.2)

ggplot(aire, aes(Month, Ozone)) + 
  geom_boxplot(alpha = 0.4, color = "dodgerblue4", outlier.shape = 19, outlier.color = "red", aes(fill = Mes)) +
  geom_point(alpha = 0.2)

Agreguemos otros elementos importantes en un gráfico, como título:

ggplot(aire, aes(Mes, Ozone)) + 
  geom_boxplot(alpha = 0.4, color = "dodgerblue4", outlier.shape = 19, outlier.color = "red", aes(fill = Month))+
  labs(title = "Media diaria de Ozono en ppm, por mes", subtitle = "Roosevel Island según mes, del 1 de mayo al 30 de septiembre 1973", y = "Ozono")

Otra variante interesante del diagrama de caja que se viene utilizando, es el gráfico de violín o violin plot, el cuál presenta la ventaja de que permite conocer la distribución de los datos durante el recorrido.

Antes de constuir nuestro primer violin plot, podemos simplificar el código almacenando instrucciones de uso frecuente, como seguidamente se muestra:

# g1 es una variables que almacena la primera capa del gráfico a crear

g1 <- ggplot(aire, aes(Mes, Ozone))

Los violinplot son un tipo de función de densidad (density plot) para los datos vistos desde arriba. Este gráfico se está prefiriendo sobre el box plot porque permite ver en el recorrido la forma en que se agrupan o se concentran los datos. No es necesario, para efectos de publicación, presentarlo junto con la caja.

Seguidamente, se construye el violin plot:

g1 + 
  geom_violin(aes(fill = Month, alpha = 0.3))

Se puede agregar los puntos para verlos superpuestos al violinplot:

g1 + 
  geom_jitter(width = 0.3, alpha = 0.4) +
    geom_violin(aes(fill = Month, alpha = 0.3))

Se puede superponer además con los box plot:

g1 + 
  geom_jitter(width = 0.3, alpha = 0.4) + 
  geom_boxplot(alpha = 0.4, color = "blue") + 
  geom_violin(aes(alpha = 0.3, fill = Month))

En el siguiente código se propone crear algunas estadísticas para los datos agrupados: cantidad de datos, desviación estándar, error estándar y lo límites superior e inferior para el intervalo de confianza para dicha media, con el objetico de incluirlos dentro del gráfico que se viene trabajando.

Para este trabajo se utilizará el operador pipe %>% que permite crear acciones anidadas:

est <- aire %>%
  group_by(Mes) %>%
  summarise(media = mean(Ozone), 
            n = n(), 
            de = sd(Ozone),
            ee = de/sqrt(n()),
            li = media - 1.96*ee,
            ls = media +1.96*ee
            )
est

Se procede a incluir estas nuevas estadísticas al gráfico:

g1 + 
  geom_jitter(width = 0.3, alpha = 0.4) + 
  geom_boxplot() + 
  geom_violin(aes(alpha = 0.3, fill = Month)) +
  theme_minimal() + 
  geom_point(data = est, aes(Mes, media)) +
  geom_errorbar(data = est, aes(Mes, media, ymin = li, ymax = ls), width = 0.2, color ="red")

Tanto los gráficos de barras como la variante de estos conocida como lollipops podrían ser útiles en el conteo de datos:

ggplot(aire, aes(Mes)) + 
  geom_bar(aes(fill = Month))

ggplot(aire, aes(Month)) + 
  geom_bar(aes(fill = Mes))

mescont <- aire %>%
  group_by(Mes) %>%
  summarize(conteo = n() )

ggplot(mescont, aes(Mes, conteo)) + 
  geom_col(fill = "steelblue", width = 0.015) + 
  geom_point(color = "darkorange", size = 5) +
  geom_text(aes(label = conteo), size = 2)

Con el paquete Plotly podemos tomar un objeto de ggplot2y mediante la función ggplotly se le puede dar cierta interactividad al gráfico.

graf1 <- ggplot(aire, aes(Month)) + 
  geom_bar(aes(fill = Mes))
ggplotly(graf1)
lollipops <- ggplot(mescont, aes(Mes, conteo)) + 
  geom_col(fill = "steelblue", width = 0.015) + 
  geom_point(color = "darkorange", size = 5) +
  geom_text(aes(label = conteo), size = 2)
ggplotly(lollipops)

Seguimoa trabajando con los gráficos de barras, pero ahora con los datos diamantes que se encuentra en el paquete datos

str(diamantes)
## tibble [53,940 x 10] (S3: tbl_df/tbl/data.frame)
##  $ precio     : int [1:53940] 326 326 327 334 335 336 336 337 337 338 ...
##  $ quilate    : num [1:53940] 0.23 0.21 0.23 0.29 0.31 0.24 0.24 0.26 0.22 0.23 ...
##  $ corte      : Ord.factor w/ 5 levels "Regular"<"Bueno"<..: 5 4 2 4 2 3 3 3 1 3 ...
##  $ color      : Ord.factor w/ 7 levels "D"<"E"<"F"<"G"<..: 2 2 2 6 7 7 6 5 2 5 ...
##  $ claridad   : Ord.factor w/ 8 levels "I1"<"SI2"<"SI1"<..: 2 3 5 4 2 6 7 3 4 5 ...
##  $ profundidad: num [1:53940] 61.5 59.8 56.9 62.4 63.3 62.8 62.3 61.9 65.1 59.4 ...
##  $ tabla      : num [1:53940] 55 61 65 58 58 57 57 55 61 61 ...
##  $ x          : num [1:53940] 3.95 3.89 4.05 4.2 4.34 3.94 3.95 4.07 3.87 4 ...
##  $ y          : num [1:53940] 3.98 3.84 4.07 4.23 4.35 3.96 3.98 4.11 3.78 4.05 ...
##  $ z          : num [1:53940] 2.43 2.31 2.31 2.63 2.75 2.48 2.47 2.53 2.49 2.39 ...
diamantesdata <- diamantes
ggplot(data = diamantesdata) +
  geom_bar(mapping = aes(x = corte)) # aes: define una variable para la estética

Podemos cambiar el orden de las barras con la función reorder

diamantesgrupo <- diamantesdata %>%
  group_by(corte) %>%
  summarize(cantidad = n()
            )
diamantesgrupo$proporcion <- diamantesgrupo$cantidad/sum(diamantesgrupo$cantidad)
ggplot(data = diamantesgrupo) +
  geom_bar(mapping = aes(x = reorder(corte, cantidad), y = cantidad) ,stat="identity", position="dodge")

Cambiando el orden de las barras:

ggplot(data = diamantesgrupo) +
  geom_bar(mapping = aes(x = reorder(corte, -cantidad), y = cantidad) ,stat="identity", position="dodge")

ggplot(data = diamantesgrupo) +
  geom_bar(mapping = aes(x = reorder(corte, -proporcion), y = proporcion, group = 1) ,stat="identity", position="dodge")

ggplot(data = diamantesgrupo) +
  geom_bar(mapping = aes(x = reorder(corte, -cantidad), y = cantidad, colour = corte) ,stat="identity", position="dodge")

Note que la instrucción colour no colorea el relleno de las barras, sino el borde. Para colorear las barras se debe usar fill

ggplot(data = diamantesgrupo) +
  geom_bar(mapping = aes(x = reorder(corte, -cantidad), y = cantidad, fill = corte) ,stat="identity", position="dodge")

Podemos agregar una tercera variable para el análisis de los datos mediante el fill:

ggplot(data = diamantesdata) +
  geom_bar(mapping = aes(x = corte, fill = color))

El apilamiento se realiza automáticamente mediante el ajuste de posición especificado por el argumento position. Se muestra un ejemplo con position = “jitter”, pero se le invita a explorar otras opciones para position: “identity”, “dodge”, “fill”, “stack”

ggplot(data = diamantesdata) +
  geom_bar(mapping = aes(x = corte, fill = color), position = "jitter")

ggplot(data = diamantes) +
  geom_bar(mapping = aes(x = corte, fill = color), position = "fill")

Si se desea las barras horrizontales, se agrega la instrucción coord_flip() y automáticamente ordena las barras de mayor a menor

ggplot(data = diamantes) +
  geom_bar(mapping = aes(x = corte, fill = corte), position = "dodge") +
  coord_flip()

En el siguiente modelo con el parámetro width se puede eliminar el espacio entre las barras:

bar <- ggplot(data = diamantes) +
  geom_bar(
    mapping = aes(x = corte, fill = corte),
    show.legend = FALSE,
    width = 1
  ) +
  theme(aspect.ratio = 1) +
  labs(x = NULL, y = NULL)
bar

En horizontal:

bar +
  coord_flip()

Otra versión interesante de los gráaficos de barra, es en coordenadas polares:

bar +
  coord_polar()

bar2 <- ggplot(data = diamantes) +
  geom_bar(mapping = aes(x = corte, fill = color),
    show.legend = F,
    width = 0.75) +
  theme(aspect.ratio = 1) +
  labs(x = NULL, y = NULL)

bar + coord_flip()

bar2 + 
  coord_polar()